/*
 * Copyright (c) 2010 Zauber S.A.  -- All rights reserved
 */
package ar.edu.itba.ia2010.geneticos.motor.api.sintaxistree.generators;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import org.apache.commons.lang.Validate;

import ar.edu.itba.ia2010.geneticos.motor.api.sintaxistree.Expresion;
import ar.edu.itba.ia2010.geneticos.motor.api.sintaxistree.ExpresionGenerator;
import ar.edu.itba.ia2010.geneticos.motor.api.sintaxistree.impl.ExpresionPrimitive;

/**
 * Clase base para {@link ExpresionGenerator}s.
 * 
 * @author Juan F. Codagnone
 * @since Mar 28, 2010
 */
public abstract class AbstractExpresionGenerator implements ExpresionGenerator {
    private final ExpresionPrimitive []all;
    private final ExpresionPrimitive []o;
    private final ExpresionPrimitive []t;
    
    
    /** Creates the FullExpresionGenerator. */
    public AbstractExpresionGenerator(final ExpresionPrimitive []primitives) {
        Validate.notNull(primitives);
        
        final List<ExpresionPrimitive> ops = new ArrayList<ExpresionPrimitive>();
        final List<ExpresionPrimitive> terms = new ArrayList<ExpresionPrimitive>();
        for(final ExpresionPrimitive e : primitives) {
            Validate.notNull(e);
            if(e.isTerminal()) {
                terms.add(e);
            } else {
                ops.add(e);
            }
        }
        
        o = ops.toArray(new ExpresionPrimitive[ops.size()]);
        t = terms.toArray(new ExpresionPrimitive[terms.size()]);
        all = primitives;
    }
    
    /** devuelve uniformemente un terminal */
    protected final Expresion nextTerminal(final Random random, final int maxDepth) {
        return rand(t, random).create(random, maxDepth, this);
    }

    /** devuelve uniformemente una operacion */
    protected final Expresion nextOperation(final Random random, 
            final int maxDepth) {
        return rand(o, random).create(random, maxDepth, this);
    }
    
    /** devuelve uniformemente una operacion */
    protected final Expresion next(final Random random, 
            final int maxDepth) {
        return rand(all, random).create(random, maxDepth, this);
    }
    
    /** trae al azar un miebro del arreglo */
    private static ExpresionPrimitive rand(final ExpresionPrimitive []e,  
            final Random random) {
        return e[e.length == 1 ? 0 : random.nextInt(e.length)];
    }
}
